/*
 * OpenParts
 * A dynamic-loading components framework for GWT
 * Copyright (C) 2011 Christophe Bouthier  [chris{AT}binary-gastronome{DOT}fr]
 *
 * This work is partially based on work I have done at INRIA (http://www.inria.fr) 
 * in the context of the Qualipso European Project (http://qualipso.org/),
 * The work done at INRIA is Copyright (C) 2006-2011 INRIA
 *
 * This work is distributed under the LGPL version 3
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation. See the GNU
 * Lesser General Public License in LGPL.txt for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package fr.openparts.OpenParts.test.gwt.tests;

import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;

import fr.openparts.OpenParts.client.OPMessageFromEmbeddedViewHandler;
import fr.openparts.OpenParts.client.OPMessagesDispatcher;
import fr.openparts.OpenParts.client.OPParams;
import fr.openparts.OpenParts.client.OPSlot;

/**
 * GWT tests for OPSlot.
 * 
 * @author Christophe Bouthier [chris{AT}binary-gastronome{DOT}fr]
 * @creation.date 11 July 2010
 */
public class OPSlotTestGwt extends GWTTestCase {

    private static final String SLOT_ID = "dummySlotId_slot";

    private OPSlot slot;

    /**
     * Return the name of the tested GWT Module.
     * 
     * @see com.google.gwt.junit.client.GWTTestCase#getModuleName()
     */
    @Override
    public final String getModuleName() {
        return "fr.openparts.OpenParts.test.gwt.TestOpenParts";
    }

    /**
     * Called at the start of each test to set up the service.
     * 
     * @see com.google.gwt.junit.client.GWTTestCase#gwtSetUp()
     */
    @Override
    protected void gwtSetUp() throws Exception {
        super.gwtSetUp();
        slot = new OPSlot(SLOT_ID);
    }

    /**
     * Test constructor passing a valid slot id. Should have something registered.
     */
    public final void testConstructor() {
        assertNotNull("Slot shouldn't be null", slot);
    }

    /**
     * Test constructor passing a null slot id. Should raise an assertion error.
     */
    public final void testConstructorWithNullSlotId() {
        if (assertionsAreEnabled()) {
            try {
                slot = new OPSlot(null);
                fail("OPSlot should raise an assertion error when constructed with a with a null slot id.");
            } catch (AssertionError e) {
                assertTrue("Error should be an AssertionError", e instanceof AssertionError);
            }
        }
    }

    /**
     * Test constructor passing an empty slot id. Should raise an assertion error.
     */
    public final void testConstructorWithEmptySlotId() {
        if (assertionsAreEnabled()) {
            try {
                slot = new OPSlot("");
                fail("OPSlot should raise an assertion error when constructed with a with an empty slot id.");
            } catch (AssertionError e) {
                assertTrue("Error should be an AssertionError", e instanceof AssertionError);
            }
        }
    }

    /**
     * Test getting back slot id. Should get back the corresponding value.
     */
    public final void testSlotId() {
        assertEquals("Slot id should be " + SLOT_ID, SLOT_ID, slot.getSlotId());
    }

    /**
     * Test getting back view proxy. Should get something not null.
     */
    public final void testViewProxy() {
        assertNotNull("View proxy shouldn't be null", slot.getViewProxy());
    }

    /**
     * Test dispatcher. Should get something not null.
     */
    public final void testGetDispatcher() {
        assertNotNull("Dispatcher shouldn't be null", getDispatcherAttachedToElementl(slot.getElement()));
    }

    /**
     * Test view proxy is registered in dispatcher.
     */
    public final void testViewProxyRegisteredInDispatcher() {
        final DummyMessageFromEmbeddedViewHandler handler = new DummyMessageFromEmbeddedViewHandler();
        slot.getViewProxy().addMessageHandler(handler);

        assertEquals("Handler should not have been called yet", 0, handler.counterOfTimeCalled);

        getDispatcherAttachedToElementl(slot.getElement()).dispatchMessageToContainerSlot("message", null);

        assertEquals("Handler should have been called now, and only once", 1, handler.counterOfTimeCalled);
        assertEquals("Handler should get message passed", "message", handler.message);
        assertEquals("Handler should get null params", null, handler.params);
        
        assertFalse("Embedded view shouldn't be attached yet", handler.isAttached);
        
        getDispatcherAttachedToElementl(slot.getElement()).onEmbeddedViewAttach();
        
        assertTrue("Embedded view should be attached now", handler.isAttached);
    }
    
    /**
     * Test adding a widget when there is no real proxy attached (view is not attached). Should add the widget.
     */
    public final void testAddingWidgetWhenViewIsNotAttached() {
        final Element slotElement = slot.getElement();
        
        assertFalse("slot should not contains any children yet", slotElement.hasChildNodes());
        
        slot.add(new Label("Loading..."));
        
        assertTrue("slot should contains one child now", slotElement.hasChildNodes());
        assertEquals("slot should contains one and only one child", 1, slotElement.getChildCount());
        assertTrue("slot child should be \"Loading...\"", slotElement.getFirstChildElement().getInnerHTML().contains("Loading..."));
    }

    /**
     * Test adding a widget when there is already a real proxy attached (view is attached). Should not add the widget.
     */
    public final void testAddingWidgetWhenViewIsAttached() {
        final Element slotElement = slot.getElement();
        
        assertFalse("slot should not contains any children yet", slotElement.hasChildNodes());
        
        final Element element = DOM.createDiv();
        element.setId(SLOT_ID);
        RootPanel.getBodyElement().appendChild(element);
        final OPMessagesDispatcher dispatcher = OPMessagesDispatcher.dispatcherFactory(SLOT_ID);
        dispatcher.attachDispatcherOnElement(element);
        registerDummyProxyInDispatcher(dispatcher);
        
        slot.add(new Label("Loading..."));
        
        assertFalse("slot should still not contains any children now", slotElement.hasChildNodes());
        
        RootPanel.getBodyElement().removeChild(element);
    }
    
    /**
     * Test adding a null widget. Should raise an assertion error.
     */
    public final void testAddingNullWidget() {
        if (assertionsAreEnabled()) {
            try {
                slot.add(null);
                fail("OPSlot should raise an assertion error when adding a null widget.");
            } catch (AssertionError e) {
                assertTrue("Error should be an AssertionError", e instanceof AssertionError);
            }
        }
    }
    
    /**
     * Return the dispatcher attached to the given element.
     * 
     * @param element
     *            the element on which to search for a dispatcher
     * @return the attached dispatcher, if any, or null if there is no dispatcher attached to this element.
     */
    private final native static OPMessagesDispatcher getDispatcherAttachedToElementl(final Element element) /*-{
        return element.messageDispatcher;
    }-*/;

    /**
     * Dummy class implementing {@link OPMessageFromEmbeddedViewHandler}.
     */
    class DummyMessageFromEmbeddedViewHandler implements OPMessageFromEmbeddedViewHandler {

        public int counterOfTimeCalled = 0;
        public String message;
        public OPParams params;
        public boolean isAttached = false;

        /**
         * Handle message from embedded view.
         * 
         * @see fr.openparts.OpenParts.client.OPMessageFromEmbeddedViewHandler#handleMessageFromEmbeddedView(java.lang.String,
         *      fr.openparts.OpenParts.client.OPParams)
         */
        @Override
        public void handleMessageFromEmbeddedView(final String message, final OPParams params) {
            ++counterOfTimeCalled;
            this.message = message;
            this.params = params;
        }

        /**
         * Called when the embedded view is attached.
         * 
         * @see fr.openparts.OpenParts.client.OPMessageFromEmbeddedViewHandler#onEmbeddedViewAttach()
         */
        @Override
        public void onEmbeddedViewAttach() {
            isAttached = true;
        }
                
    }
    
    /**
     * Register a dummy slot proxy in the dispatcher.
     * 
     * @param dispatcher
     *            the dispatcher on which to register the proxy.
     */
    private final native void registerDummyProxyInDispatcher(final OPMessagesDispatcher dispatcher) /*-{
        dispatcher.messageFromContainerSlotHandlerCallback = 1;
    }-*/;

    /**
     * Check if assertions are enabled.
     * 
     * @return true if assertions are enabled, false otherwise
     */
    private final boolean assertionsAreEnabled() {
        try {
            assert false;
            return false;
        } catch (AssertionError e) {
            return true;
        }
    }

}
